import weasyprint
import os
import json
import hashlib
import re
import sys

from weasyprint import HTML
from pathlib import Path
from datetime import datetime
from utils import get_config


class Report(object):

    def __init__(self, capture_directory, frontend):
        self.capture_directory = capture_directory
        self.alerts = self.read_json(os.path.join(
            capture_directory, "assets/alerts.json"))
        self.whitelist = self.read_json(os.path.join(
            capture_directory, "assets/whitelist.json"))
        self.conns = self.read_json(os.path.join(
            capture_directory, "assets/conns.json"))
        self.device = None
        self.capinfos = None
        if frontend:
            self.device = self.read_json(os.path.join(
                capture_directory, "assets/device.json"))
            self.capinfos = self.read_json(os.path.join(
                capture_directory, "assets/capinfos.json"))
        try:
            with open(os.path.join(self.capture_directory, "capture.pcap"), "rb") as f:
                self.capture_sha1 = hashlib.sha1(f.read()).hexdigest()
        except:
            self.capture_sha1 = "N/A"

        self.userlang = get_config(("frontend", "user_lang"))

        # Load template language
        if not re.match("^[a-z]{2,3}$", self.userlang):
            self.userlang = "en"
        with open(os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), "locales/{}.json".format(self.userlang))) as f:
            self.template = json.load(f)["report"]

    def read_json(self, json_path):
        """
            Read and convert a JSON file.
            :return: array or dict.
        """
        with open(json_path, "r") as json_file:
            return json.load(json_file)

    def generate_report(self):
        """
            Generate the full report in PDF
            :return: nothing
        """
        content = self.generate_page_header()
        content += self.generate_header()
        content += self.generate_warning()
        content += self.generate_alerts()
        content += self.generate_suspect_conns_block()
        content += self.generate_uncat_conns_block()
        content += self.generate_whitelist_block()

        htmldoc = HTML(string=content, base_url="").write_pdf()
        Path(os.path.join(self.capture_directory,
                          "report.pdf")).write_bytes(htmldoc)

    def generate_warning(self):
        """
            Generate the warning message.
            :return: str
        """
        if len(self.alerts["high"]):
            msg = "<div class=\"warning high\">"
            msg += self.template["high_msg"].format(
                self.nb_translate(len(self.alerts["high"])))
            msg += "</div>"
            return msg
        elif len(self.alerts["moderate"]):
            msg = "<div class=\"warning moderate\">"
            msg += self.template["moderate_msg"].format(
                self.nb_translate(len(self.alerts["moderate"])))
            msg += "</div>"
            return msg
        elif len(self.alerts["low"]):
            msg = "<div class=\"warning low\">"
            msg += self.template["low_msg"].format(
                self.nb_translate(len(self.alerts["low"])))
            msg += "</div>"
            return msg
        else:
            msg = "<div class=\"warning low\">"
            msg += self.template["none_msg"]
            msg += "</div>"
            return msg

    def nb_translate(self, nb):
        """
            Translate a number in a string.
            :return: str
        """
        a = self.template["numbers"]
        return a[nb-1] if nb <= 9 else str(nb)

    def generate_suspect_conns_block(self):
        """
            Generate the table of the network non-whitelisted communications.
            :return: string
        """

        if not len([c for c in self.conns if c["alert_tiggered"] == True]):
            return ""

        title = "<h2>{}</h2>".format(self.template["suspect_title"])
        table = "<table>"
        table += "    <thead>"
        table += "        <tr>"
        table += "             <th>{}</th>".format(self.template["protocol"])
        table += "             <th>{}</th>".format(self.template["domain"])
        table += "             <th>{}</th>".format(self.template["dst_ip"])
        table += "             <th>{}</th>".format(self.template["dst_port"])
        table += "        </tr>"
        table += "    </thead>"
        table += "<tbody>"

        for rec in self.conns:
            if rec["alert_tiggered"] == True:
                table += "<tr>"
                table += "<td>{}</td>".format(rec["proto"].upper())
                table += "<td>{}</td>".format(rec["resolution"]
                                              if rec["resolution"] != rec["ip_dst"] else "--")
                table += "<td>{}</td>".format(rec["ip_dst"])
                table += "<td>{}</td>".format(rec["port_dst"])
                table += "</tr>"
        table += "</tbody></table>"
        return title + table

    def generate_uncat_conns_block(self):
        """
            Generate the table of the network non-whitelisted communications.
            :return: string
        """
        if not len([c for c in self.conns if c["alert_tiggered"] == False]):
            return ""

        title = "<h2>{}</h2>".format(self.template["uncat_title"])
        table = "<table>"
        table += "    <thead>"
        table += "        <tr>"
        table += "             <th>{}</th>".format(self.template["protocol"])
        table += "             <th>{}</th>".format(self.template["domain"])
        table += "             <th>{}</th>".format(self.template["dst_ip"])
        table += "             <th>{}</th>".format(self.template["dst_port"])
        table += "        </tr>"
        table += "    </thead>"
        table += "<tbody>"

        for rec in self.conns:
            if rec["alert_tiggered"] == False:
                table += "<tr>"
                table += "<td>{}</td>".format(rec["proto"].upper())
                table += "<td>{}</td>".format(rec["resolution"]
                                              if rec["resolution"] != rec["ip_dst"] else "--")
                table += "<td>{}</td>".format(rec["ip_dst"])
                table += "<td>{}</td>".format(rec["port_dst"])
                table += "</tr>"
        table += "</tbody></table>"
        return title + table

    def generate_whitelist_block(self):
        """
            Generate the table of the whitelisted communications.
            :return: string
        """
        if not len(self.whitelist):
            return ""

        title = "<h2>{}</h2>".format(self.template["whitelist_title"])
        table = "<table>"
        table += "    <thead>"
        table += "        <tr>"
        table += "             <th>{}</th>".format(self.template["protocol"])
        table += "             <th>{}</th>".format(self.template["domain"])
        table += "             <th>{}</th>".format(self.template["dst_ip"])
        table += "             <th>{}</th>".format(self.template["dst_port"])
        table += "        </tr>"
        table += "    </thead>"
        table += "<tbody>"

        for rec in sorted(self.whitelist, key=lambda k: k['resolution']):
            table += "<tr>"
            table += "<td>{}</td>".format(rec["proto"].upper())
            table += "<td>{}</td>".format(rec["resolution"]
                                          if rec["resolution"] != rec["ip_dst"] else "--")
            table += "<td>{}</td>".format(rec["ip_dst"])
            table += "<td>{}</td>".format(rec["port_dst"])
            table += "</tr>"
        table += "</tbody></table>"
        return title + table

    def generate_header(self):
        """
            Generate the report header with context data.
            :return: string
        """
        header = "<div class=\"header\">"
        header += "<div class=\"logo\"></div>"
        if self.device is not None:
            header += "<p><br /><strong>{}: {}</strong><br />".format(self.template["device_name"],
                                                                  self.device["name"])
            header += "{}: {}<br />".format(self.template["device_mac"],
                                        self.device["mac_address"])
        header += "{} {}<br />".format(self.template["report_generated_on"],
                                       datetime.now().strftime("%d/%m/%Y - %H:%M:%S"))
        if self.capinfos is not None:
            header += "{}: {}s<br />".format(self.template["capture_duration"],
                                             self.capinfos["Capture duration"])
            header += "{}: {}<br />".format(self.template["packets_number"],
                                            self.capinfos["Number of packets"])
        header += "{}: {}<br />".format(
            self.template["capture_sha1"], self.capture_sha1)
        header += "</p>"
        header += "</div>"
        return header

    def generate_alerts(self):
        """
            Generate the alerts.
            :return: string
        """
        alerts = "<ul class=\"alerts\">"
        for alert in self.alerts["high"]:
            alerts += "<li class =\"alert\">"
            alerts += "<span class=\"high-label\">High</span>"
            alerts += "<span class=\"alert-id\">{}</span>".format(alert["id"])
            alerts += "<div class = \"alert-body\">"
            alerts += "<span class=\"title\">{}</span>".format(alert["title"])
            alerts += "<p class=\"description\">{}</p>".format(
                alert["description"])
            alerts += "</div>"
            alerts += "</li>"

        for alert in self.alerts["moderate"]:
            alerts += "<li class =\"alert\">"
            alerts += "<span class=\"moderate-label\">moderate</span>"
            alerts += "<span class=\"alert-id\">{}</span>".format(alert["id"])
            alerts += "<div class = \"alert-body\">"
            alerts += "<span class=\"title\">{}</span>".format(alert["title"])
            alerts += "<p class=\"description\">{}</p>".format(
                alert["description"])
            alerts += "</div>"
            alerts += "</li>"
        for alert in self.alerts["low"]:
            alerts += "<li class =\"alert\">"
            alerts += "<span class=\"low-label\">low</span>"
            alerts += "<span class=\"alert-id\">{}</span>".format(alert["id"])
            alerts += "<div class = \"alert-body\">"
            alerts += "<span class=\"title\">{}</span>".format(alert["title"])
            alerts += "<p class=\"description\">{}</p>".format(
                alert["description"])
            alerts += "</div>"
            alerts += "</li>"

        alerts += "</ul>"
        return alerts

    def generate_page_footer(self):
        """
            Generate the html footer.
            :return: string
        """
        return "</body></html>"

    def generate_page_header(self):
        """
            Generate the html header.
            :return: string
        """
        return """<html
                    <head>
                        <style>
                            * {
                                font-family: Arial, Helvetica, sans-serif;
                            }

                            h2 {
                                padding-top: 30px;
                                font-weight: 400;
                                font-size: 18px;
                            }

                            td {
                                width: auto;
                                padding: 10px;
                            }

                            table {
                                background: #FFF;
                                border: 2px solid #FAFAFA;
                                border-radius: 5px;
                                border-collapse: separate;
                                border-spacing: 0px;
                                width: 100%;
                                font-size: 12px;
                            }

                            p {
                                font-size: 13px;
                            }

                            thead tr th {
                                border-bottom: 1px solid #CCC;
                                border-collapse: separate;
                                border-spacing: 5px 5px;
                                background-color: #FFF;
                                padding: 10px;
                                text-align: left;
                            }

                            tbody tr#first td {
                                border-top: 3px solid #4d4d4d;
                                border-collapse: separate;
                                border-spacing: 5px 5px;
                            }

                            tr:nth-of-type(odd) {
                                background-color: #fafafa;
                            }

                            .logo {
                                background-image: url("");
                                width: 200px;
                                height: 60px;
                                background-size: cover;
                                position: absolute;
                                right: 0px;
                            }

                            .warning {
                                padding: 10px;
                                text-align: center;
                                border-radius: 5px;
                                color: #FFF;
                                margin-top: 40px;
                                margin-bottom: 40px;
                                font-weight:900;
                            }

                            .high {
                                background-color: #F44336;
                            }

                            .moderate {
                                background-color: #ff7e33eb;
                            }

                            .low {
                                background-color: #4fce0eb8;
                            }

                            ul {
                                list-style: none;
                                margin: 0;
                                padding: 0;
                            }

                            .alert {
                                margin-top: 15px;
                            }

                            .alert-body {
                                background-color: #FFF;
                                list-style: none;
                                padding: 10px;
                                border-radius: 5px;
                                border: 1px solid #EEE;
                                margin-top: 3px;
                            }

                            .alert-body>.title {
                                display: block;
                                padding: 5px 5px 5px 10px;
                                font-size: 13px;
                            }

                            .high-label {
                                background-color: #F44336;
                                padding: 5px;
                                text-transform: uppercase;
                                font-size: 10px;
                                font-weight: bold;
                                border-radius: 3px 0px 0px 0px;
                                margin: 0px;
                                color: #FFF;
                                margin-left: 10px;
                            }

                            .moderate-label {
                                background-color: #ff7e33eb;
                                padding: 5px;
                                text-transform: uppercase;
                                font-size: 10px;
                                font-weight: bold;
                                border-radius: 3px 0px 0px 0px;
                                margin: 0px;
                                color: #FFF;
                                margin-left: 10px;
                            }

                            .low-label {
                                background-color: #4fce0eb8;
                                padding: 5px;
                                text-transform: uppercase;
                                font-size: 10px;
                                font-weight: bold;
                                border-radius: 3px 0px 0px 0px;
                                margin: 0px;
                                color: #FFF;
                                margin-left: 10px;
                            }

                            .description {
                                margin: 0;
                                padding: 10px;
                                color:#333;
                                font-size:12px;
                            }

                            ul {
                                list-style: none;
                                margin: 0;
                                padding: 0;
                            }

                            .alert-id {
                                background-color: #636363;
                                padding: 5px;
                                text-transform: uppercase;
                                font-size: 10px;
                                font-weight: bold;
                                border-radius: 0px 3px 0px 0px;
                                margin: 0px;
                                color: #FFF;
                                margin-right: 10px;
                            }
                 
                            .header>p {
                                font-size:12px;
                            }
                            @page {
                                @top-center { 
                                    content: "REPORT_HEADER - Page " counter(page) " / " counter(pages) ".";
                                    font-size:12px;
                                    color:#CCC;
                                }
                                @bottom-center { 
                                    content: "REPORT_FOOTER";
                                    font-size:12px;  
                                    color:#CCC;
                                }
                            }   
                        </style>
                    </head>
                    <body>""".replace("REPORT_HEADER", "{} {}".format(self.template["report_for_the_capture"], self.capture_sha1)).replace("REPORT_FOOTER", self.template["report_footer"])
